package org.opens.lambda.future;

import org.junit.Test;

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

public class FutureCallbackEvent {

    /**
     * 功能:
     *      测试自定义的异步回调机制的Callable对象调用.
     * 输出:
     *      finished
     *      主线程结束了
     * 说明:
     *      1. 其实也就是将主线程要做的事情一起交给异步线程去做, 异步线程执行完开始异步执行我们注册的方法.
     */
    @Test
    public void test1() throws InterruptedException {
        Future<String> future = invoke(() -> {
            try {
                Thread.sleep(3000);
                return "finished";
            } catch (InterruptedException e) {
                e.printStackTrace();
                return "error";
            }
        });

        future.setCompletable(new Completable<String>() {
            @Override
            public void complete(String s) {
                System.out.println(s);
            }

            @Override
            public void exception(Throwable throwable) {
                System.out.println("错误");
                throwable.printStackTrace();
            }
        });
        Thread.sleep(6000);
        System.out.println("主线程结束了");
    }

    private static <T> Future<T> invoke(Callable<T> callable) {

        //原子操作类, 可以实现安全的保存操作
        AtomicReference<T> result = new AtomicReference<>();
        AtomicBoolean finished = new AtomicBoolean(false);

        Future<T> future = new Future<T>() {

            private Completable<T> completable;

            @Override
            public T get() {
                return result.get();
            }

            @Override
            public boolean isDone() {
                return finished.get();
            }

            @Override
            public void setCompletable(Completable<T> completable) {
                this.completable = completable;
            }

            @Override
            public Completable<T> getCompletable() {
                return this.completable;
            }
        };

        Thread thread = new Thread(() -> {
            try {
                T value = callable.action();
                result.set(value);
                finished.set(true);
                if(future.getCompletable() != null) {
                    future.getCompletable().complete(value);
                }
            } catch (Throwable throwable) {
                if(future.getCompletable() != null) {
                    future.getCompletable().exception(throwable);
                }
            }
        });

        thread.start();
        return future;
    }

    private interface Future<T> {

        T get();

        boolean isDone();

        void setCompletable(Completable<T> completable);

        Completable<T> getCompletable();

    }

    private interface Callable<T> {

        T action();

    }

    private interface Completable<T> {

        void complete(T t);

        void exception(Throwable throwable);

    }

}
